﻿using BCCommon;
using BCCommon.Enums;
using BCCommon.ExceptionExtensions;
using BCCommon.Extensions;
using BCCommon.PaymentEnums;
using BCCommon.TMS.Order;
using BCCommon.TMS.PaymentEnums;
using BCDto.Common.Payment.Payment;
using BCDto.TMS.Order;
using BCService.Common.Payment.Payment;
using BCService.Common.Payment.PaymentRecord;
using BCService.TMS.Order.Order;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace BCTMSRESTful.Controllers.ClientApp.Payment
{
    [Route("api/v1/ClientApp/[controller]")]
    [ApiExplorerSettings(GroupName = "ClientApp")]
    public class PaymentController : ClientAppBaseController
    {
        private readonly IOrderProcessService orderProcessService;
        private readonly IPaymentService paymentService;
        private readonly ICommonPaymentRecordService commonPaymentRecordService;
        public PaymentController(
            IOrderProcessService orderProcessService,
            IPaymentService paymentService,
            ICommonPaymentRecordService commonPaymentRecordService)
        {
            this.orderProcessService = orderProcessService;
            this.paymentService = paymentService;
            this.commonPaymentRecordService = commonPaymentRecordService;
        }


        #region 获取提现渠道
        /// <summary>
        /// 获取可用的付款渠道。
        /// </summary>
        /// <param name="osType">操作系统。</param>
        /// <param name="deviceType">设备类型。</param>
        /// <response code="200">付款渠道列表。</response>
        /// <returns></returns>
        [HttpGet("[action]")]
        [ProducesResponseType(typeof(IEnumerable<ChannelDto>), 200)]
        public IActionResult TransferChannels([FromQuery(Name = "os_type")] SourceOSType osType, [FromQuery(Name = "device_type")] DeviceType deviceType)
        {
            PaymentChannelType[] supportedChannels = new PaymentChannelType[] { PaymentChannelType.Alipay, PaymentChannelType.Weixin };

            var channels = this.paymentService.GetChannels(SourceAppType.ClientApp, FlowType.Transfer, osType, this.UserInfo).Where(p => p.ChannelId.AsEnum<PaymentChannelType>().In(supportedChannels));

            return this.Ok(channels);
        }
        #endregion

        #region 获取可用支付渠道
        /// <summary>
        /// 获取可用的付款渠道。
        /// </summary>
        /// <param name="orderType">订单类型。</param>
        /// <param name="osType">操作系统。</param>
        /// <param name="deviceType">设备类型。</param>
        /// <response code="200">付款渠道列表。</response>
        /// <returns></returns>
        [HttpGet("[action]")]
        [ProducesResponseType(typeof(IEnumerable<ChannelDto>), 200)]
        public IActionResult Channels([FromQuery(Name = "order_type")] FlowType orderType, [FromQuery(Name = "os_type")] SourceOSType osType, [FromQuery(Name = "device_type")] DeviceType deviceType)
        {
            var channels = this.paymentService.GetChannels(SourceAppType.ClientApp, orderType, osType, this.UserInfo);

            return this.Ok(channels);
        }
        #endregion

        /// <summary>
        /// 统一付款接口，创建付款请求。
        /// </summary>
        /// <param name="requestDto">付款请求信息。</param>
        /// <response code="200">付款结果。</response>
        /// <returns></returns>
        [HttpPost]
        [ProducesResponseType(typeof(PaymentDto), 200)]
        public IActionResult Create([FromBody] PaymentRequestDto requestDto)
        {
            var ip = Request.Headers["X-Forwarded-For"].FirstOrDefault();
            if (string.IsNullOrEmpty(ip))
            {
                ip = HttpContext.Connection.RemoteIpAddress.ToString();
            }
            IChannelFactoryService channelService = this.paymentService.GetChannelService(SourceAppType.ClientApp, requestDto.ChannelType, requestDto.OSType);

            if (channelService == null)
            {
                throw new ArgumentException("选定的付款渠道暂时无法使用。");
            }

            if (requestDto.OrderType == FlowType.TMSOrder)
            {
                OrderDto order = this.orderProcessService.GetOrder(requestDto.ReferenceNumber);

                if (order == null)
                {
                    throw new ArgumentException($"找不到编号为 {requestDto.ReferenceNumber} 的订单");
                }

                if (order.OrderStatus == OrderStatus.Cancel)
                {
                    throw new ArgumentException("该订单已被取消，不能为该订单进行付款");
                }

                if (order.OrderAmount <= 0)
                {
                    throw new ArgumentException("该订单暂时没有待付款的费用");
                }

                // 增加订单并发校验
                //this.clientOrderService.CheckOrderDriverVehicle(order);

                if (this.orderProcessService.CheckOrderPaid(requestDto.ReferenceNumber))
                {
                    throw new ArgumentException("订单已支付，无需重复操作");
                }
                this.UserInfo.AddressIp = ip;
                string subjectTitle = string.Format("用户支付{0}运费,订单编号:{1}", requestDto.OrderType.GetDisplayString(), requestDto.ReferenceNumber);
                PaymentDto paymentDto = channelService.TryPay(SourceAppType.ClientApp, requestDto.OrderType, subjectTitle, requestDto.ReferenceNumber, requestDto.OSType, requestDto.DeviceType, requestDto.DeviceId, requestDto.Parameters, order.OrderAmount.Value, requestDto.Payer, this.UserInfo);

                return this.Ok(paymentDto);
            }

            if (requestDto.OrderType == FlowType.Recharge)
            {
                if (requestDto.Amount <= 0)
                {
                    throw new ArgumentException("充值金额必须大于 0 元。");
                }
                this.UserInfo.AddressIp = ip;
                string rechargeNo = RandomCode.GeneratePaymentOrderNo();
                string subjectTItle = string.Format("用户{0}:{1}元", requestDto.OrderType.GetDisplayString(), requestDto.Amount);
                PaymentDto paymentDto = channelService.TryPay(SourceAppType.ClientApp, requestDto.OrderType, subjectTItle, rechargeNo, requestDto.OSType, requestDto.DeviceType, requestDto.DeviceId, requestDto.Parameters, requestDto.Amount,requestDto.Payer, this.UserInfo);

                return this.Ok(paymentDto);
            }

            throw new ArgumentException($"用户端不支持类型为 {requestDto.OrderType.GetDisplayString()} 的订单付款。");
        }

        /// <summary>
        /// 同步订单状态。
        /// </summary>
        /// <param name="paymentRecordId">支付记录编号。</param>
        /// <param name="requestDto"></param>
        /// <response code="200">付款结果。</response>
        /// <returns></returns>
        [HttpPost("{payment_record_id}/[action]")]
        [ProducesResponseType(typeof(PaymentDto), 200)]
        public IActionResult Synchronize([FromRoute(Name = "payment_record_id")] long paymentRecordId, [FromBody] PaymentSynchronizeRequestDto requestDto)
        {
            var paymentRecord = commonPaymentRecordService.GetPaymentRecord(paymentRecordId);

            if (paymentRecord == null)
            {
                throw new ObjectNotFoundException();
            }

            var channelFactoryService = this.paymentService.GetChannelService(SourceAppType.ClientApp, paymentRecord.PaymentChannelId.AsEnum<PaymentChannelType>(), paymentRecord.OSType.AsEnum<SourceOSType>());

            if (channelFactoryService == null)
            {
                throw new ArgumentException("原订单的付款渠道暂时无法使用。");
            }

            paymentRecord = channelFactoryService.Synchronize(paymentRecordId, requestDto);

            if (paymentRecord.CheckSignTime == null && paymentRecord.Status == PaymentResultType.Success)
            {
                // 同步原始订单状态
                this.paymentService.Synchronize(this.UserInfo, paymentRecord);
            }

            var paymentDto = new PaymentDto
            {
                PaymentRecordId = paymentRecord.PaymentRecordId,
                OrderType = paymentRecord.OrderType,
                ReferenceNumber = paymentRecord.ReferenceNumber,
                PayChannelType = paymentRecord.PaymentChannelId,
                OriginalAmount = paymentRecord.Amount,
                Discount = paymentRecord.Discount,
                Result = paymentRecord.Status
            };

            if (paymentDto.Result == PaymentResultType.Success)
            {
                paymentDto.PaidAmount = paymentRecord.Amount - paymentRecord.Discount;
                paymentDto.PaidTime = paymentRecord.SuccessTime;
            }

            return this.Ok(paymentDto);
        }
        #region 获取提现预授权信息
        /// <summary>
        /// 获取预授权登录信息。
        /// </summary>
        /// <param name="dto">预授渠道权信息。</param>
        /// <response code="200">预授权结果。</response>
        /// <returns></returns>
        [HttpPost("[action]")]
        [ProducesResponseType(typeof(Dictionary<string, string>), 200)]
        public IActionResult AppAuthLogin([FromBody] AuthLoginRequestDto dto)
        {
            IChannelFactoryService channelFactoryService = this.paymentService.GetChannelService(SourceAppType.ClientApp, dto.ChannelType, dto.OSType);

            if (channelFactoryService == null)
            {
                throw new ArgumentException("选定的授权渠道暂时无法使用。");
            }
            return this.Ok(channelFactoryService.CreateAppAuthLogin(SourceAppType.ClientApp, dto.OSType, this.UserInfo));
        }

        /// <summary>
        /// 授权登录用户的相关信息。
        /// </summary>
        /// <param name="dto">预授渠道权信息。</param>
        /// <response code="200">预授权结果。</response>
        /// <returns></returns>
        [HttpPost("[action]")]
        [ProducesResponseType(typeof(Dictionary<string, string>), 200)]
        public IActionResult AuthLoginInfo([FromBody] AuthLoginInfoRequestDto dto)
        {
            IChannelFactoryService channelFactoryService = this.paymentService.GetChannelService(SourceAppType.ClientApp, dto.ChannelType, dto.OSType);

            if (channelFactoryService == null)
            {
                throw new ArgumentException("选定的授权渠道暂时无法使用。");
            }
            return this.Ok(channelFactoryService.AuthLoginInfo(dto.Parameters["auth_code"], SourceAppType.ClientApp, dto.OSType, this.UserInfo));
        }
        #endregion
    }
}
